package com.haust.controller;

import com.haust.config.WeChatConfig;
import com.haust.entity.User;
import com.haust.entity.VideoOrder;
import com.haust.pojo.JsonData;
import com.haust.service.UserService;
import com.haust.service.VideoOrderService;
import com.haust.util.JWTUtils;
import com.haust.util.WXPayUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.util.Date;
import java.util.Map;

/**
 * @Auther: csp1999
 * @Date: 2020/08/27/15:17
 * @Description: 微信相关 API
 */
@Controller
@RequestMapping("/wechat")
public class WeChatController {

    @Autowired
    private WeChatConfig weChatConfig;

    @Autowired
    private UserService userService;

    @Autowired
    private VideoOrderService videoOrderService;

    /**
     * @方法描述: 扫码登录,拼装扫一扫登录url
     * @参数集合: [accessPage]
     * @返回类型: com.haust.pojo.JsonData
     * @作者名称: csp1999
     * @日期时间: 2020/8/27 16:45
     */
    @ResponseBody
    @GetMapping("/login_url")
    @CrossOrigin
    public JsonData weChatloginUrl(@RequestParam(value = "state", required = true) String state) throws UnsupportedEncodingException {
        /**
         * state :
         * 用于保持请求和回调的状态，授权请求后原样带回给第三方。该参数可用于防
         * 止csrf攻击（跨站请求伪造攻击），建议第三方带上该参数，可设置为简单的随
         * 机数加session进行校验,例如：state=3d6be0a4035d839573b04816624a415e
         */
        // 获取开放平台重定向地址
        String redirectUrl = weChatConfig.getOpenRedirectUrl();

        // 微信开放平台文档规定,需要先对回调的url使用urlEncode对链接进行编码处理
        String callbackUrl = URLEncoder.encode(redirectUrl, "GBK");

        // 为扫码链接qrcodeUrl填充参数 appid=%s redirect_uri=%s state=%s 到 OPEN_QRCODE_URL
        String qrcodeUrl = String.format(weChatConfig.getOpenQrcodeUrl(), weChatConfig.getOpenAppid(), callbackUrl, state);

        // 构建json对象返回
        return JsonData.buildSuccess(qrcodeUrl);
    }

    /**
     * @方法描述: 通过扫码登录跳转页面携带的参数code而获取封装有user信息的token
     * @参数集合: [code, state, response]
     * @返回类型: com.haust.pojo.JsonData
     * @作者名称: csp1999
     * @日期时间: 2020/8/27 19:17
     */
    @GetMapping("/user/callback")
    @CrossOrigin
    public void weChatUserCallback(@RequestParam(value = "code", required = true) String code,
                                   String state, // 根据实际情况而定可用作保存当前页面地址
                                   RedirectAttributes redirectAttributes, HttpServletResponse response) throws IOException {
        User user = userService.saveWeChatUser(code);

        String token = null;
        if (user != null) {
            // jwt 生成 token
            token = JWTUtils.createJsonWebToken(user);
            //redirectAttributes.addFlashAttribute("token",token);
            //redirectAttributes.addFlashAttribute("state",state);
            // 将token 拼接于url ,便于拦截器过滤
            response.sendRedirect(state + "?token=" + token + "&head_img=" + user.getHeadImg() + "&name="
                    + URLEncoder.encode(user.getName(), "UTF-8"));
        }
    }

    /**
     * @方法描述: 微信支付成功之后回调
     * @参数集合: [request, response]
     * @返回类型: void
     * @作者名称: csp1999
     * @日期时间: 2020/8/29 20:57
     */
    @CrossOrigin
    @RequestMapping("/order/callback")// 注意：不能写GetMapper 微信支付开发文档上有声明，可以读文档了解详情
    public void orderCallBack(HttpServletRequest request, HttpServletResponse response) throws Exception {
        // 通过request 获取输入流
        InputStream in = request.getInputStream();
        // 通过该 字节输入流 获取缓冲流 :BufferedReader 是一个包装设计模式，性能更高
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(in, "UTF-8"));

        // 读取数据
        StringBuffer stringBuffer = new StringBuffer();// 用于拼接并拿到微信平台 发送的请求中的xml 格式的数据
        String line;
        while ((line = bufferedReader.readLine()) != null) {
            stringBuffer.append(line);
        }

        // 关闭所有流
        bufferedReader.close();
        in.close();

        Map<String, String> callbackMap = WXPayUtil.xmlToMap(stringBuffer.toString());
        System.out.println("-------------------- 拿到微信平台 发送的请求中的xml 格式的数据：--------------------");
        System.out.println(callbackMap.toString());

        // 判断签名是否正确(跟官网校验的方式一样，xml串 和 商户key)
        if (WXPayUtil.isSignatureValid(callbackMap, weChatConfig.getKey())) {
            System.out.println("签名校验通过...");
            if ("SUCCESS".equals(callbackMap.get("result_code"))) {
                // result_code: 业务结果	SUCCESS/FAIL
                // 根据流水号查找订单
                VideoOrder dbVideoOrder = videoOrderService.findByVideoOrderOutTradeNo(callbackMap.get("out_trade_no"));
                if (dbVideoOrder.getState() == 0) {// 判断业务场景: 支付状态是0，即未支付时候才可以进行下一步操作
                    VideoOrder videoOrder = new VideoOrder();
                    videoOrder.setOpenid(callbackMap.get("openid"))// 用户标识
                            .setOutTradeNo(callbackMap.get("out_trade_no"))// 微信支付流水号
                            .setNotifyTime(new Date())// 支付回调时间
                            .setTotalFee(Integer.parseInt(callbackMap.get("total_fee")))// 支付总金额
                            .setState(1);// 支付状态改为已经支付

                    // 根据流水号更新订单
                    int row = videoOrderService.updateVideoOderByOutTradeNo(videoOrder);

                    // 判断影响行数 row == 1/row == 0 更新订单成功/失败
                    if (row == 1) {
                        System.out.println("----------------------- row=" + row + "-----------------------");
                        // 成功: 通知微信后台 订单处理成功
                        response.setContentType("text/xml");
                        response.getWriter().println("success");// SUCCESS：表示告诉微信后台，网站平台成功接收到其通知并在自己的后台校验成功
                    }
                }
            }
        }

        // 失败: 通知微信后台 订单处理失败
        response.setContentType("text/xml");
        response.getWriter().println("fail");// FAIL：表示告诉微信后台，网页后台校验失败
    }
}
